package com.sky.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.constant.PasswordConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.dto.LoginResultDTO;
import com.sky.entity.Employee;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.result.PageResult;
import com.sky.service.EmployeeService;
import com.sky.utils.TokenUtils;
import com.sky.vo.EmployeeVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // Token过期时间：2小时
    private static final long TOKEN_EXPIRE_HOURS = 2;

    /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public LoginResultDTO login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        // 1. 根据用户名查询数据库
        Employee employee = employeeMapper.getByUsername(username);

        // 2. 校验用户信息（异常处理），单独抽取做下面的方法
        validateEmployee(employee, password);

        // 3. 生成token并存储到Redis（单独抽取另写一个方法）
        String token = TokenUtils.generateToken();
        saveTokenToRedis(token, employee.getId());

        // 4. 封装返回结果（转换为VO，避免敏感信息）
        return buildLoginResult(token, employee);
    }

    /**
     * 校验员工信息（抽取为独立方法，提高可读性）
     */
    private void validateEmployee(Employee employee, String inputPassword) {
        if (employee == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // 密码加密校验（实际业务必须加密，这里补充完整）数据库也要用同样的加密方式，不然双方不一致
        // String encryptedPassword = DigestUtils.md5DigestAsHex(inputPassword.getBytes());
        // 数据库处理好加密就把inputPassword替换成encryptedPassword相比较
        if (!inputPassword.equals(employee.getPassword())) {
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }
    }

    /**
     * 存储token到Redis（抽取为独立方法，职责单一）
     */
    private void saveTokenToRedis(String token, Long employeeId) {
        // key: token，value: employeeId
        String redisKey = TokenUtils.buildTokenKey(token);
        stringRedisTemplate.opsForValue().set(
                redisKey,
                employeeId.toString(),
                TOKEN_EXPIRE_HOURS,
                TimeUnit.HOURS
        );
    }

    /**
     * 构建登录结果（转换实体为VO，封装token）
     */
    private LoginResultDTO buildLoginResult(String token, Employee employee) {
        LoginResultDTO result = new LoginResultDTO();
        //赋值实体类token
        result.setToken(token);

        // 转换Employee为EmployeeVO
        EmployeeVO employeeVO = new EmployeeVO();
        BeanUtils.copyProperties(employee, employeeVO);  // 复制同名属性
        // 若有特殊字段映射，可手动设置（如employee.getRealName() -> employeeVO.setName()）
        result.setEmployee(employeeVO);
        //返回LoginResultDTO对象给上一层
        return result;
    }

    // 登出方法（补充）
    public void logout(String token) {
        if (token == null) {
            return;
        }
        //删除redis缓存
        stringRedisTemplate.delete(TokenUtils.buildTokenKey(token));
    }


    @Override
    public void addEmp(EmployeeDTO dto) {
        log.info("EmployeeServiceImpl:线程id={}", Thread.currentThread().getId());
        Employee employee = new Employee();
        // 属性拷贝
        BeanUtils.copyProperties(dto, employee);

        // 1.补充缺失的属性值
        // 补充密码字段，需要进行MD5加密
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
        employee.setStatus(StatusConstant.ENABLE);

        // 使用公共字段自动填充逻辑
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        // // 从ThreadLocal中获取出登陆人id
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());

        // 2.调用mapper的新增方法，将员工对象存入employee表中
        employeeMapper.insert(employee);
    }

    /**
     * 员工分页查询---需要使用分页插件PageHelper
     * @param dto
     * @return
     */
    @Override
    public PageResult page(EmployeePageQueryDTO dto) {
        // 1.设置分页参数
        PageHelper.startPage(dto.getPage(), dto.getPageSize());

        // 2.调用mapper的查询方法,并强转返回类型为Page
        Page<Employee> page = employeeMapper.list(dto.getName());

        // 3.封装PageResult对象并返回
        return new PageResult(page.getTotal(), page.getResult());
    }

    /**
     * 启用禁用员工
     * @param status
     * @param id
     */
    @Override
    public void enableOrDisable(Integer status, Long id) {
        // 链式编程，传递id和需要更新的属性值，供给下方mapper方法，如果更新值则会更新，id一般不会更新
        // 实体类需要打Builder注解
        Employee employee = Employee.builder()
                .id(id)
                .status(status)
                .updateTime(LocalDateTime.now())
                .updateUser(BaseContext.getCurrentId())
                .build();
        employeeMapper.update(employee);
    }

    @Override
    public Employee getById(Long id) {
        //密码赋值给其他的返回
        Employee employee = employeeMapper.getById(id);
        // 增加非空判断，避免空指针异常，没有修改到数据库，只是赋值
        if (employee != null) {
            employee.setPassword("******");
        }
        return employeeMapper.getById(id);
    }

    @Override
    public void update(EmployeeDTO dto) {
        //因为DTO字段是前端参数，字段不全，需要构造员工对象进行补全
        Employee employee = new Employee();
        // 拷贝属性值，将dto拷贝到employee对象中，保持字段与employee对象中一致
        // 这一步会将 DTO 和 实体对象中名称相同的属性自动赋值，覆盖操作，用来做新的查询条件
        BeanUtils.copyProperties(dto, employee);
        // 拷贝完成后，手动补充更新时间和更新人值，这样sql语句不会不更新这两个值
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(BaseContext.getCurrentId());

        employeeMapper.update(employee);
    }

}
